/*
 * Copyright 2018-2019 Redis Labs Ltd. and Contributors
 *
 * This file is available under the Redis Labs Source Available License Agreement
 */

#include "relocate_op.h"

/* Locate the closest projection or aggregation op
 * in the execution chain of the given operation. */
static OpBase *_locateProjection(OpBase *root) {
	if(!root) return NULL;

	if(root->type == OPType_PROJECT || root->type == OPType_AGGREGATE) {
		return root;
	}

	for(int i = 0; i < root->childCount; i++) {
		OpBase *op = _locateProjection(root->children[i]);
		if(op) return op;
	}

	return NULL;
}

/* Migrate the given operation directly above the project/aggregate operation. */
void _relocateOp(ExecutionPlan *plan, OPType type) {
	OpBase *op = ExecutionPlan_LocateOp(plan->root, type);
	if(!op) return;
	assert(op->childCount == 1);

	// Find the first projection or aggregation in the operation's chain
	OpBase *projection_op = _locateProjection(op->children[0]);
	assert(projection_op);

	// Remove op from its current position.
	ExecutionPlan_RemoveOp(plan, op);
	// Push op right above projection/aggregation
	ExecutionPlan_PushBelow(projection_op, op);
}

void relocateOperations(ExecutionPlan *plan) {
	assert(plan && plan->root);
	_relocateOp(plan, OPType_LIMIT);
	_relocateOp(plan, OPType_SKIP);
	_relocateOp(plan, OPType_SORT);
	_relocateOp(plan, OPType_DISTINCT);
}
